﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using OpenPhotoAlbum.DbAccess;

namespace OpenPhotoAlbum.BusinessLogics
{
    public class UserManager
    {
        #region Singleton

        private static readonly UserManager _instance = new UserManager();

        static UserManager() { }

        private UserManager() { }

        public static UserManager Instance
        {
            get { return _instance; }
        }

        #endregion

        public User GetUser(DbLinqClassesDataContext context, Guid userId)
        {
            var objects = from u in context.Users
                          where u.UserId.Equals(userId)
                          select u;
            if (objects != null && objects.Count() > 0)
            {
                return objects.First();
            }
            return null;
        }

        public User GetUser(DbLinqClassesDataContext context, string userName)
        {
            var objects = from u in context.Users
                          where u.UserName.ToLower() == userName.ToLower()
                          select u;
            if (objects != null && objects.Count() > 0)
            {
                return objects.First();
            }
            return null;
        }

        public User GetUser(Guid userId)
        {
            DbLinqClassesDataContext context = new DbLinqClassesDataContext();
            return GetUser(context, userId);
        }

        public User GetUser(string userName)
        {
            DbLinqClassesDataContext context = new DbLinqClassesDataContext();
            return GetUser(context, userName);
        }

        public User GetUserByEmail(string userName, string userEmail)
        {
            DbLinqClassesDataContext context = new DbLinqClassesDataContext();
            var results = from u in context.Users
                          where u.UserEmail != null
								&& u.UserName.ToLower() == userName.ToLower()		
								&& u.UserEmail.ToLower() == userEmail.ToLower()
                          select u;
            if (results == null || results.Count() == 0) { return null; }
            
            return results.First();
        }

        public IQueryable<User> GetUsers()
        {
            DbLinqClassesDataContext context = new DbLinqClassesDataContext();
            var users = from u in context.Users
                        select u;
            return users;
        }

        public bool IsUserNameUsed(string userName)
        {
            DbLinqClassesDataContext context = new DbLinqClassesDataContext();
            return GetUser(context, userName) != null;
        }

        public Guid CreateNewUser(string userName, string userPassword, string userEmail)
        {
            DbAccess.DbLinqClassesDataContext context = new DbAccess.DbLinqClassesDataContext();
            DbAccess.User user = new DbAccess.User();
            user.UserId = Guid.NewGuid();
            user.UserName = userName;
            user.UserPassword = userPassword;
            user.UserEmail = userEmail;
			user.UserRegDate = DateTime.UtcNow;
            user.UserLastLogOnDate = user.UserRegDate;
            user.UserTotalLogOnCount = 1;
            user.RoleName = RoleNames.CommonUser;
            context.Users.InsertOnSubmit(user);
            context.SubmitChanges();

			ActionManager.Instance.AddUserRegisteration(userName);

            return user.UserId;
        }

        public void PromoteUserToAdministrator(Guid userId)
        {
            DbLinqClassesDataContext context = new DbLinqClassesDataContext();

            Role role = GetRole(context, RoleNames.Administrator);
            if (role == null) throw new RoleNotFoundException();

            User user = GetUser(context, userId);
            user.Role = role;
            context.SubmitChanges();
        }

        public void DeleteUser(Guid userId)
        {
            DbLinqClassesDataContext context = new DbLinqClassesDataContext();
            User user = GetUser(context, userId);
            if (user != null)
            {
                context.Users.DeleteOnSubmit(user);
                context.SubmitChanges();
            }
        }

        public void UpdateLastLogonDate(Guid userId)
        {
            DbLinqClassesDataContext context = new DbLinqClassesDataContext();
            User user = GetUser(context, userId);
			user.UserLastLogOnDate = DateTime.UtcNow;
            context.SubmitChanges();
        }

		public void ChangePassword(Guid userId, string newPassword)
		{
			DbLinqClassesDataContext context = new DbLinqClassesDataContext();
			ChangePassword(context, userId, newPassword);
			context.SubmitChanges();

			ActionManager.Instance.AddPasswordChange(GetUser(userId).UserName);
		}

        public void ChangePassword(DbLinqClassesDataContext context, Guid userId, string newPassword)
        {
            User user = GetUser(context, userId);
            user.UserPassword = newPassword;
        }

		public void ChangeEmail(Guid userId, string newEmail)
		{
			DbLinqClassesDataContext context = new DbLinqClassesDataContext();
			DbAccess.User user = GetUser(context, userId);
			user.UserEmail = newEmail;
			context.SubmitChanges();

			ActionManager.Instance.AddEmailChange(user.UserName, newEmail);
		}

        public bool ValidateUser(string userName, string password)
        {
            DbLinqClassesDataContext context = new DbLinqClassesDataContext();
            var users = from u in context.Users
                        where u.UserName.ToLower() == userName.ToLower() && u.UserPassword == password
                        select u;
            return users != null && users.Count() == 1;
        }

        public bool IsAdministrator(string userName)
        {
            return IsInRole(userName, RoleNames.Administrator);
        }

        public bool IsUser(string userName)
        {
            return IsInRole(userName, RoleNames.CommonUser);
        }

        private bool IsInRole(string userName, string roleName)
        {
            User user = GetUser(userName);
            if (user != null)
            {
                return user.RoleName.Equals(
                    roleName,
                    StringComparison.InvariantCultureIgnoreCase);
            }
            return false;
        }

        private Role GetRole(DbLinqClassesDataContext context, string roleName)
        {
            var roles = from r in context.Roles
                        where r.RoleName == roleName
                        select r;
            if (roles != null && roles.Count() > 0)
            {
                return roles.First();
            }
            return null;
        }

		public void UpdateUserIcon(Guid userId, byte[] userIcon)
		{
			DbLinqClassesDataContext context = new DbLinqClassesDataContext();
			User user = GetUser(context, userId);
			user.UserIcon = new System.Data.Linq.Binary(userIcon);
			context.SubmitChanges();
		}

		public bool IsUserLoggedIn
		{
			get { return !string.IsNullOrEmpty(HttpContext.Current.User.Identity.Name); }
		}

		public int GetUserPhotoCount(Guid userId)
		{
			DbLinqClassesDataContext context = new DbLinqClassesDataContext();
			var results = from p in context.Photos
						  where p.Album.UserId.Equals(userId)
						  select p.PhotoId;
			return results.Count();
		}

		public int GetUserCommentCount(Guid userId)
		{
			return GetUser(userId).Comments.Count;
		}

		private List<string> illegalWords = null;

		public bool IsUserNameLegal(string userName)
		{
			if (illegalWords == null)
			{
				illegalWords = new List<string>();
				illegalWords.AddRange(FilterConfigReader.Instance.ReadGeneralFilter().Split('|'));
				illegalWords.AddRange(FilterConfigReader.Instance.ReadUserNameFilter().Split('|'));
			}
			userName = userName.ToLower();
			foreach (string word in illegalWords)
			{
				if (string.IsNullOrEmpty(word)) { continue; }
				else if (userName.Contains(word)) { return false; }
			}
			return true;
		}

		private const int USERNAME_MAX_LENGTH = 45;
		private const int PASSWORD_MAX_LENGTH = 45;
		private const int EMAIL_MAX_LENGTH = 180;

		public bool IsUserNameTooLong(string userName)
		{
			return userName.Length > USERNAME_MAX_LENGTH;
		}

		public bool IsPasswordTooLong(string password)
		{
			return password.Length > PASSWORD_MAX_LENGTH;
		}

		public bool IsEmailTooLong(string email)
		{
			return email.Length > EMAIL_MAX_LENGTH;
		}
    }

    public static class RoleNames
    {
        public static readonly string Administrator = "Administrator";
        public static readonly string CommonUser = "CommonUser";
    }
}
